package org.dromara.common.web.handler

import cn.hutool.http.HttpStatus
import com.fasterxml.jackson.core.JsonParseException
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.servlet.ServletException
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.ConstraintViolation
import jakarta.validation.ConstraintViolationException
import org.dromara.common.core.domain.R
import org.dromara.common.core.domain.R.Companion.fail
import org.dromara.common.core.exception.ServiceException
import org.dromara.common.core.exception.SseException
import org.dromara.common.core.exception.base.BaseException
import org.dromara.common.core.utils.StreamUtils.join
import org.dromara.common.json.utils.JsonUtils.toJsonString
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.validation.BindException
import org.springframework.validation.ObjectError
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.MissingPathVariableException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
import org.springframework.web.servlet.NoHandlerFoundException
import java.io.IOException

/**
 * 全局异常处理器
 *
 * @author LikeYouDo
 * @date 2025/07/27
 */
@RestControllerAdvice
class GlobalExceptionHandler {
    companion object {
        private val log = KotlinLogging.logger { }
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException::class)
    fun handleHttpRequestMethodNotSupported(
        e: HttpRequestMethodNotSupportedException,
        request: HttpServletRequest
    ): R<Void?> {
        val requestURI = request.requestURI
        log.error { "请求地址'$requestURI',不支持'${e.method}'请求" }
        return fail(HttpStatus.HTTP_BAD_METHOD, e.message)
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException::class)
    fun handleServiceException(e: ServiceException, request: HttpServletRequest?): R<Void?> {
        log.error { e.message }
        val code = e.code
        return if (code != null) fail(code, e.message) else fail(e.message)
    }

    /**
     * 认证失败
     */
    @ResponseStatus(org.springframework.http.HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(SseException::class)
    fun handleNotLoginException(e: SseException, request: HttpServletRequest): String {
        val requestURI = request.requestURI
        log.debug { "请求地址'$requestURI',认证失败'${e.message}',无法访问系统资源" }
        return toJsonString(fail<Any?>(HttpStatus.HTTP_UNAUTHORIZED, "认证失败，无法访问系统资源"))
    }

    /**
     * servlet异常
     */
    @ExceptionHandler(ServletException::class)
    fun handleServletException(e: ServletException, request: HttpServletRequest): R<Void?> {
        val requestURI = request.requestURI
        log.error(e) { "请求地址'$requestURI',发生未知异常." }
        return fail(e.message)
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(BaseException::class)
    fun handleBaseException(e: BaseException, request: HttpServletRequest?): R<Void?> {
        log.error { e.message }
        return fail(e.message)
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException::class)
    fun handleMissingPathVariableException(e: MissingPathVariableException, request: HttpServletRequest): R<Void?> {
        val requestURI = request.requestURI
        log.error { "请求路径中缺少必需的路径变量'$requestURI',发生系统异常." }
        return fail("请求路径中缺少必需的路径变量[${e.variableName}]")
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException::class)
    fun handleMethodArgumentTypeMismatchException(
        e: MethodArgumentTypeMismatchException,
        request: HttpServletRequest
    ): R<Void?> {
        val requestURI = request.requestURI
        log.error { "请求参数类型不匹配'$requestURI',发生系统异常." }
        return fail(
            "请求参数类型不匹配，参数[${e.name}]要求类型为：'${e.requiredType!!.getName()}'，但输入值为：'${e.value}'"
        )
    }

    /**
     * 找不到路由
     */
    @ExceptionHandler(NoHandlerFoundException::class)
    fun handleNoHandlerFoundException(e: NoHandlerFoundException, request: HttpServletRequest): R<Void?> {
        val requestURI = request.requestURI
        log.error { "请求地址'$requestURI'不存在." }
        return fail(HttpStatus.HTTP_NOT_FOUND, e.message)
    }

    /**
     * 拦截未知的运行时异常
     */
    @ResponseStatus(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(IOException::class)
    fun handleRuntimeException(e: IOException?, request: HttpServletRequest) {
        val requestURI = request.requestURI
        if (requestURI.contains("sse")) {
            // sse 经常性连接中断 例如关闭浏览器 直接屏蔽
            return
        }
        log.error(e) { "请求地址'$requestURI',连接中断" }
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException::class)
    fun handleRuntimeException(e: RuntimeException, request: HttpServletRequest): R<Void?> {
        val requestURI = request.requestURI
        log.error(e) { "请求地址'$requestURI',发生未知异常." }
        return fail(e.message)
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception::class)
    fun handleException(e: Exception, request: HttpServletRequest): R<Void?> {
        val requestURI = request.requestURI
        log.error(e) { "请求地址'$requestURI',发生系统异常." }
        return fail(e.message)
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException::class)
    fun handleBindException(e: BindException): R<Void?> {
        log.error { e.message }
        val message =
            join<ObjectError?>(e.allErrors, { obj: ObjectError? -> obj!!.defaultMessage }, ", ")
        return fail(message)
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(ConstraintViolationException::class)
    fun constraintViolationException(e: ConstraintViolationException): R<Void?> {
        log.error { e.message }
        val message = join<ConstraintViolation<*>?>(
            e.constraintViolations,
            { obj: ConstraintViolation<*>? -> obj!!.message },
            ", "
        )
        return fail(message)
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun handleMethodArgumentNotValidException(e: MethodArgumentNotValidException): R<Void?> {
        log.error { e.message }
        val message = join<ObjectError?>(
            e.bindingResult.allErrors,
            { obj: ObjectError? -> obj!!.defaultMessage },
            ", "
        )
        return fail(message)
    }

    /**
     * JSON 解析异常（Jackson 在处理 JSON 格式出错时抛出）
     * 可能是请求体格式非法，也可能是服务端反序列化失败
     */
    @ExceptionHandler(JsonParseException::class)
    fun handleJsonParseException(e: JsonParseException, request: HttpServletRequest): R<Void?> {
        val requestURI = request.requestURI
        log.error { "请求地址'${requestURI}' 发生 JSON 解析异常: ${e.message}" }
        return fail(HttpStatus.HTTP_BAD_REQUEST, "请求数据格式错误（JSON 解析失败）：" + e.message)
    }

    /**
     * 请求体读取异常（通常是请求参数格式非法、字段类型不匹配等）
     */
    @ExceptionHandler(HttpMessageNotReadableException::class)
    fun handleHttpMessageNotReadableException(
        e: HttpMessageNotReadableException,
        request: HttpServletRequest
    ): R<Void?> {
        log.error { "请求地址'${request.requestURI}', 参数解析失败: ${e.message}" }
        return fail<Void?>(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误：" + e.getMostSpecificCause().message)
    }

}
